#include "module.h"
#include "debug.h"
#include "level.h"
#include "room.h"
#include "line.h"
#include "states.h"
#include "projectile.h"
#include "util.h"      // cli_parse::DrawPostProcessing (...)
#include "render.h"    // Underlight::Draw (...)
#include "../states.h"
#include "../console.h"

#include "../camera.h" // e3dCamera

#include "../input.h"  // eTB_Input
#include "../window.h" // eTB_RenderWindow

#include "../../Epsilon/types.h"
#include "../sound.h"  // OpenAL ...

#include "../../Epsilon/Epsilon3D/rendercontext.h"
#include "../../Epsilon/timer.h" // eTimer

cli_parse::Debug*        cli_parse::debug  = NULL;
cli_parse::ModuleStates* cli_parse::states = NULL;

cli_parse::ModuleStates*
cli_parse::GetModuleStates (void)
{
  return states;
}

namespace cli_parse {
  class Input : public eTB_Input {
  public:
             Input (eWindow* win) : eTB_Input (win) {
             };
    virtual ~Input (void) { };

    virtual void OnMouse (Epsilon::Mouse now, Epsilon::Mouse prev) {
      if (IsMouseButtonDown (eInput::Mouse::Left, &prev) !=
          IsMouseButtonDown (eInput::Mouse::Left, &now) &&
          IsMouseButtonDown (eInput::Mouse::Left, &now)) {
        states->cl_need_pick = true;
      }

      if (IsMouseButtonDown (eInput::Mouse::Right, &prev) !=
          IsMouseButtonDown (eInput::Mouse::Right, &now) &&
          IsMouseButtonDown (eInput::Mouse::Right, &now)) {
            /* This is a hackish way of printing FPS when
                 the stats overlay is disabled, it can help rule
                   out freetype performance bottlenecks. */
            if (! ::states->r_stat_overlay) {
              eTB_Printf ("Current framerate: %2.3f FPS\n",
                ::states->window->rc->GetFPS ());
            }

/// Undefined: Why? Where?
///        void print_camera_pos (void);
///        print_camera_pos ();
      }
    }

    virtual bool OnKeyPress   (Epsilon::Keyboard::Key& key)
    {
      if (eInput::OnKeyPress (key))
        return true;

      /* Shorthand access to the persistent key state array. */
      bool*      last_key = ::states->key_states;
      eTB_Input* input    = ::states->input;

      /* TODO: Move target_room to cli_parse::states... */
      static signed short target_room = 1;

      //key.getType () == Epsilon::Keyboard::Misc
      //key.getCode () == Epsilon::Keyboard::MiscKeys::Tab

#define eKeyboard Epsilon::Keyboard
#define eMiscKeys eKeyboard::MiscKeys
#define eTextKeys eKeyboard::TextKeys
#define eTextKey  eKeyboard::TextKey

      eKeyboard* keyboard = ::states->window->win->getKeyboard ();

      // TODO - Add logic to map keys to commands, rather than
      //          hard coding this stuff!

      if (! (::states->gui_console)->IsActive ())
      {
        // Tilde
        if (key.getType () == eKeyboard::Text &&
            key.getCode () == eTextKeys::WHATTHEHELL) {
          /* Issue a Command rather than manipulating the state directly,
               because there is some pre-processing work that must happen
                 before r_tex_align can be enabled... the command will
                   take care of this for us.
          */
          ::states->command->ProcessCommandLine ("r_tex_align toggle");
        }

        // P
        if (key.getType () == eKeyboard::Text &&
            key.getCode () == eTextKeys::P) {
          ::states->command->ProcessCommandLine ("cl_pause toggle");
        }

        // L  -- Lighting
        if (key.getType () == eKeyboard::Text &&
            key.getCode () == eTextKeys::L) {
          ::states->command->ProcessCommandLine ("r_lights toggle");
        }

        // H  -- HDR
        if (key.getType () == eKeyboard::Text &&
            key.getCode () == eTextKeys::H) {
          ::states->command->ProcessCommandLine ("r_hdr toggle");
        }

        // B -- Deferred Buffers (for debugging)
        if (key.getType () == eKeyboard::Text &&
            key.getCode () == eTextKeys::B) {
          if (cli_parse::states->r_deferred_mode == 1)
            ::states->command->ProcessCommandLine ("r_deferred_mode 2");
          else
            ::states->command->ProcessCommandLine ("r_deferred_mode 1");
        }

        // Q -- Quit deferred mode
        if (key.getType () == eKeyboard::Text &&
            key.getCode () == eTextKeys::Q) {
              ::states->command->ProcessCommandLine ("r_deferred_mode 0");
        }

        // O -- Toggle Screen Space Ambient [O]cclusion.
        if (key.getType () == eKeyboard::Text &&
            key.getCode () == eTextKeys::O) {
              ::states->command->ProcessCommandLine ("r_ssao toggle");
        }

        // F -- Toggle flying
        if (key.getType () == eKeyboard::Text &&
            key.getCode () == eTextKeys::F) {
              ::states->command->ProcessCommandLine ("cl_fly toggle");
        }

        // C -- Toggle collisions
        if (key.getType () == eKeyboard::Text &&
            key.getCode () == eTextKeys::C) {
              ::states->command->ProcessCommandLine ("cl_collision toggle");
        }

        // G -- Toggle gravity
        if (key.getType () == eKeyboard::Text &&
            key.getCode () == eTextKeys::G) {
              ::states->command->ProcessCommandLine ("cl_gravity toggle");
        }

        if (key.getType () == eKeyboard::Text &&
            key.getCode () == eTextKeys::LeftBracket) {
          target_room--;

          if (target_room < 0)
            target_room = 0;

          e3dVector2 vPos = Underlight::lvl->rooms [target_room]->bound.Center ();

          cli_parse::states->cl_player.Teleport (target_room, vPos.x, vPos.y);
        }

        if (key.getType () == eKeyboard::Text &&
            key.getCode () == eTextKeys::RightBracket) {
          target_room++;

          if (target_room > (int)(Underlight::lvl->rooms.size () - 1))
            target_room = (Underlight::lvl->rooms.size () - 1);

          e3dVector2 vPos = Underlight::lvl->rooms [target_room]->bound.Center ();

          cli_parse::states->cl_player.Teleport (target_room, vPos.x, vPos.y);
        }

  /* Cycle through visual effects to debug (Default Key: +/-)... */

  extern int SelectNextVisualEffect (int);
  extern int SelectPrevVisualEffect (int);

        if (key.getType () == eKeyboard::Text &&
            key.getCode () == eTextKeys::Equals) {
          cli_parse::states->r_debug_effect_id = 
          SelectNextVisualEffect (cli_parse::states->r_debug_effect_id);
        }

        if (key.getType () == eKeyboard::Text &&
            key.getCode () == eTextKeys::Minus) {
          cli_parse::states->r_debug_effect_id = 
          SelectPrevVisualEffect (cli_parse::states->r_debug_effect_id);
        }

        if (key.getType () == eKeyboard::Text &&
            key.getCode () == eTextKeys::BackSlash) {
          ::states->command->ProcessCommandLine ("r_wireframe toggle");
        }

        /* Cycle texgen modes (for debugging)... */
        if (key.getType () == eKeyboard::Text &&
            key.getCode () == eTextKeys::T) {
          // Currently only two texgen algorithms are included...
           if (states->r_texgen == 0)
             ::states->command->ProcessCommandLine ("r_texgen 1");
           else
             ::states->command->ProcessCommandLine ("r_texgen 0");
        }

        /* Fire a Projectile. */
        if (key.getType () == eKeyboard::Misc &&
            key.getCode () == eMiscKeys::Space) {
          e3dCamera* cam = cli_parse::states->cl_player.Camera ();
          cli_parse::FireProjectile (cam->GetPos (), cam->fwd_vec () * 3.3f);
        }

        if (key.getType () == eKeyboard::Misc &&
            key.getCode () == eMiscKeys::Escape) {
#ifndef USE_GPROF
          ::states->LoadResources ();
#else
          invoke_exit = true;
#endif
        }
      }

      return false;
    }

    virtual bool OnKeyUp (Epsilon::Keyboard::Key& key)
    {
      return eInput::OnKeyRelease (key);
    }
  };
};

void
cli_parse::Init (void)
{
  debug  = new cli_parse::Debug        (true);
  states = new cli_parse::ModuleStates ();

  ::states->mod_states = states;

  bool CALLBACK LoadResources_cli_parse (void*);
  void CALLBACK HandleCamera_cli_parse  (void);
  bool CALLBACK TimedUpdates_cli_parse  (void);
  void CALLBACK DrawFrame_cli_parse     (void);
  bool CALLBACK PrimaryDraw_cli_parse   (void);
  void CALLBACK Terminate_cli_parse     (int);

  ::states->handle_camera  = &HandleCamera_cli_parse;
  ::states->timed_updates  = &TimedUpdates_cli_parse;
  ::states->primary_draw   = &PrimaryDraw_cli_parse;
  ::states->render_frame   = &DrawFrame_cli_parse;
  ::states->load_resources = &LoadResources_cli_parse;
  ::states->terminate      = &Terminate_cli_parse;

  ::states->window->Create (_T ("Epsilon Testbed  [cli_parse 2.0]"), 0, 0);
  ::states->input   = new Input (::states->window->win);

  e3dCamera* camera = new e3dCamera ();

  states->cl_player.SetCamera (camera);
  states->cl_player.SetInput  (::states->input);

  states->render_context = ::states->window->rc;

  states->sound_context = new OpenALContext ();
  states->sound_context->init ();

  /* Special Case : Load resource files, but NO level data! */
  int lvl_num = -2;

  /* Initialize the tesselator, even if the config file does not requrie it. */
  states->r_tesselate_triangles = true;

  /* Setup resources. */
  ::states->LoadResources (&lvl_num);

  /* Load the config file. */
  ::states->command->ProcessCommandLine ("source config.eps");
}


#include "level.h"
#include "room.h"
Underlight::File* ul_file = NULL;

#include "textures.h"  // Underlight::TextureManager

extern void eTB_BringConsoleToFront (void);
extern void eTB_BringWindowToFront  (void);

bool
load_level (int lvl_num)
{
  cli_parse::debug->MemoryCheckpoint (_T ("Begin load_level"));

  Underlight::Level* old_lvl  = NULL;
  Underlight::Room*  old_room = NULL;

  old_lvl = 
    Underlight::lvl;

  if (old_lvl)
    old_room = old_lvl->rooms [cli_parse::states->cl_cam_room];

  if (! Underlight::IsLevelLoaded (lvl_num))  {
    if (! ul_file->Load (lvl_num)) {
      eTB_Printf (_T ("Could not load level: %d...\n"), lvl_num);
      return false;
    }
  } else {
    if (Underlight::GetLevel (lvl_num) == NULL)
      return false;
  }

  cli_parse::states->cl_level_num = lvl_num;
  Underlight::lvl = Underlight::GetLevel (lvl_num);

//  if (old_room)
//    old_room->Unload ();

  cli_parse::states->cl_level_num = lvl_num;

  cli_parse::states->cl_cam_room = 1;
  e3dVector2 vPos = Underlight::lvl->rooms [1]->bound.Center ();

  Underlight::lvl->rooms [1]->Load (Underlight::lvl);

  cli_parse::states->cl_player.Teleport (1, vPos.x, vPos.y);

  cli_parse::debug->MemoryCheckpoint (_T ("End load_level"));

  return true;
}

#include "../../Epsilon/Epsilon3D/Epsilon3D.h"
#include "../../Epsilon/Epsilon3D/rendercontext.h"
#include "../../Epsilon/Epsilon3D/OpenGL.h"

#define g_Window ::states->window

void
draw_splash_screen (void)
{
  g_Window->rc->Resize     (0, 0);

  states->BeginFrame       ();
  g_Window->rc->BeginFrame ();
  {
    g_Window->rc->Clear (e3dRenderStates::ColorBuffer | e3dRenderStates::DepthBuffer);
    extern void SetupView (e3dViewport* viewport);
    SetupView (&g_Window->viewport);

    extern void e3dLoadIdentity (void);
    e3dLoadIdentity ();

  struct OverlayVertex
  {
    float         pos   [4];
    unsigned char color [4];
  };

  OverlayVertex aOverlayVertices [4];

  OverlayVertex* pVert = aOverlayVertices;

  float color = 1.0f;

  for (int i = 0; i < 4; i++) {
            aOverlayVertices [i].pos [2] = 0.0f;
            aOverlayVertices [i].pos [3] = 1.0f; // w = 1.0f
    (DWORD&)aOverlayVertices [i].color   = D3DCOLOR_COLORVALUE (0.0f, 0.0f, 0.0f, color);
  }

  pVert->pos [0] = 0.0f;
  pVert->pos [1] = 0.0f;

  pVert++;

  pVert->pos [0] = 0.0f;
  pVert->pos [1] = states->window->viewport.Height ();

  pVert++;

  pVert->pos [0] = states->window->viewport.Width ();
  pVert->pos [1] = 0.0f;

  pVert++;

  pVert->pos [0] = states->window->viewport.Width  ();
  pVert->pos [1] = states->window->viewport.Height ();

  pVert = aOverlayVertices;

  glPushAttrib       (GL_ALL_ATTRIB_BITS);
  glPushClientAttrib (GL_CLIENT_ALL_ATTRIB_BITS);

  glMatrixMode   (GL_MODELVIEW);
  glPushMatrix   ();
  glLoadIdentity ();

  glMatrixMode   (GL_PROJECTION);
  glPushMatrix   ();
  glLoadIdentity ();

  #define g_Window ::states->window

  GLint viewport [4];
  glGetIntegerv (GL_VIEWPORT, viewport);

  glOrtho (0, g_Window->viewport.Width (), 0, g_Window->viewport.Height (), -1.0f, 1.0f);

  glDisable (GL_DEPTH_TEST);

  glEnable (GL_COLOR_MATERIAL);

  glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glDisable (GL_DEPTH_TEST);
  glDisable (GL_CULL_FACE);
  glDisable (GL_BLEND);
  glBlendFunc (GL_ZERO, GL_SRC_ALPHA);

  cli_parse::states->render_context->SetCullFaces (e3dRenderStates::False);
  cli_parse::states->render_context->SetDepthTest (e3dRenderStates::False);

//  glDisable (GL_DEPTH_TEST);
//  glDisable (GL_CULL_FACE);

  glBegin (GL_TRIANGLE_STRIP);

  glColor4f (1.0f, 1.0f, 1.0f, 1.0f);

  glVertex2f (aOverlayVertices [0].pos [0],
              aOverlayVertices [0].pos [1]);
  glTexCoord2f (0.0f, 1.0f);

  glVertex2f (aOverlayVertices [1].pos [0],
              aOverlayVertices [1].pos [1]);
  glTexCoord2f (1.0f, 0.0f);

  glVertex2f (aOverlayVertices [2].pos [0],
              aOverlayVertices [2].pos [1]);
  glTexCoord2f (1.0f, 1.0f);

  glVertex2f (aOverlayVertices [3].pos [0],
              aOverlayVertices [3].pos [1]);
  glTexCoord2f (0.0f, 1.0f);

  glEnd ();

  glDisable (GL_BLEND);

  glMatrixMode (GL_PROJECTION);
  glPopMatrix  ();

  glMatrixMode (GL_MODELVIEW);
  glPopMatrix  ();

  glPopClientAttrib ();
  glPopAttrib       ();

  cli_parse::states->render_context->SetCullFaces (e3dRenderStates::True);
  cli_parse::states->render_context->SetDepthTest (e3dRenderStates::True);

//  glClearColor (0.01, 0.01f, 0.01f, 1.0f);
//  glClear (GL_COLOR_BUFFER_BIT);
  }
  g_Window->rc->EndFrame ();
  states->EndFrame ();
}


bool
pick_level (void)
{
//  eTB_BringConsoleToFront ();

  extern bool ApplyEffectTexture (unsigned int);
  ApplyEffectTexture (2045);

  draw_splash_screen ();

  eTB_Printf (_T ("Please enter the level number... "));
  eTB_Scanf  (_T ("%d"), &cli_parse::states->cl_level_num);

  if (cli_parse::states->cl_level_num > 46/*level->NumLevels ()*/ ||
      cli_parse::states->cl_level_num < 1)
    return false;

  bool ret = load_level (cli_parse::states->cl_level_num);

  if (ret) {
    //
    // Search for a formal entry point
    //
    //  * Teleport to it, if one is found.
    //
    std::vector<cli_parse::EntryPoint*>::const_iterator entry =
        Underlight::lvl->entry_points.begin ();
    std::vector<cli_parse::EntryPoint*>::const_iterator end   =
        Underlight::lvl->entry_points.end ();

    while (eSTL::IterValid (entry, end)) {
      if ((*entry)->GetType () == 0) {
        float x     = (*entry)->GetX     ();
        float y     = (*entry)->GetY     ();
        float angle = (*entry)->GetAngle ();
        int   room  = (*entry)->GetRoom  ();

        cli_parse::states->cl_player.Teleport (room, x, y, angle);
      }

      entry++;
    }
  }

  return ret;
}

#include "effect.h"

void
load_ul3d (const TCHAR* filename, int lvl_num)
{
  _tcscpy (cli_parse::states->cli_file_name, filename);

  if (! ul_file) {
    Underlight::textures->LoadTextureFile (_T ("Textures.rlm"));

    eTB_Printf (_T ("Visual Effects: %3.2f Kb\n"),
                  Underlight::effects->VEBytes () / 1024.0f);

    //Init4DX (1,1);

    ul_file = new Underlight::File (filename);
  }

  /* We just wanted to load the resource files, not a level... */
  if (lvl_num == -2) {
    return;
  }

  if (lvl_num == -1) {
    // Pick level until it returns true...
    bool ret = false;

    do {
      ret = pick_level ();
    } while (! ret);

    eTB_BringWindowToFront ();
  } else {
    load_level (lvl_num);
  }
}


#include "tesselation.h" // tess:: ...

bool CALLBACK
LoadResources_cli_parse (void* usr_data)
{
  cli_parse::debug->MemoryCheckpoint (_T ("Begin LoadResources"));

  tess::InitTesselator (cli_parse::states->r_tesselate_triangles);

  if (usr_data == NULL)
    load_ul3d (_T ("game.cli"/*C:\\Program Files\\Underlight\\game.cli"*/), -1);
  else {
    load_ul3d (_T ("game.cli"/*"C:\\Program Files\\Underlight\\game.cli"*/), *((int *)usr_data));

    /* If -2, we only want to setup the resource files...
         --> do not load anything. */
    if (*(int *)usr_data == -2)
      return true;
  }

  for (unsigned int i = 1; i < Underlight::lvl->rooms.size (); i++) {
    Underlight::Room* room = Underlight::lvl->rooms [i];

    eTB_Printf (_T ("Room: %#2d (%#32s) <Size: %3.1f Kb>\n"), room->id,
                  room->name, room->MemSize () / 1024.0f);
  }

  eTB_Printf ("\n");

  eTB_Printf ("Level: %s (%d)\n",       Underlight::lvl->name, Underlight::lvl->id);
  eTB_Printf (" >> Size: <%3.1f Kb>\n", Underlight::lvl->MemSize () / 1024.0f);

  cli_parse::debug->MemoryCheckpoint (_T ("End LoadResources"));

  return true;
}


void CALLBACK
Terminate_cli_parse (int reason)
{
  cli_parse::debug->MemoryCheckpoint (_T ("Module Termination"));

  // ...

  cli_parse::debug->DumpMemoryLeaks ();
}


#include "projectile.h"

bool CALLBACK
TimedUpdates_cli_parse (void)
{
  if (! cli_parse::states->cl_pause)
    cli_parse::UpdateProjectiles ();

  return true;
}


#include "../../Epsilon/debug.h"

int
GameError (int reason) 
{
  if (eMessageBoxExF (_T ("An error occured!"),
                        E_MB_YESNO | E_MB_ICONHAND,
                      _T ("Error: %d\n\n Continue?"),
                        reason)   == E_IDNO) {
    BreakPoint ();
  }

  return 1;
}


void CALLBACK DrawFrame_cli_parse (void)
{
  /* 06/15/2011 -- Why was this here? */
  //g_Window->rc->Resize     (0, 0);

  states->BeginFrame       ();
  g_Window->rc->BeginFrame ();
  {
    g_Window->rc->Clear (e3dRenderStates::ColorBuffer | e3dRenderStates::DepthBuffer);
    extern void SetupView (e3dViewport* viewport);
    SetupView (&g_Window->viewport);

    // 6/17/2010 - this probably is not necessary anymore ... disabling it!
    //extern void e3dLoadIdentity (void);
    //e3dLoadIdentity ();

    if (! states->PrimaryDraw ())
      states->PrimaryDraw ();

    states->gui_console->Display (states->console);

    /* Stuff like the FPS meter, and debug overlays... */
    cli_parse::DrawPostProcessing ();

    states->input->DrawCursor ();
  }
  g_Window->rc->EndFrame ();
  states->EndFrame       ();
}

bool CALLBACK PrimaryDraw_cli_parse (void)
{
  Underlight::Draw (Underlight::lvl, g_Window->rc);

  if (cli_parse::states->cl_need_pick) {
    cli_parse::states->cl_need_pick = false;
    PickingData pick = Underlight::Pick (states->input, g_Window->rc);

    if (pick.room <= Underlight::lvl->rooms.size () && pick.room > 0) {
      if ((unsigned short)pick.surface <= Underlight::lvl->sectors.size ()) {
        cli_parse::states->cl_picked_sector = pick;
      } else {
        cli_parse::states->cl_picked_line   = pick;

        Underlight::Room* room = Underlight::lvl->GetRoom (pick.room);
        Underlight::Line* line = Underlight::GetLine (room, pick.surface - MAX_SECTORS);

        if (line && line->IsTripLine ()) {
          DumpLine (line);

          Underlight::TripLine* trip_line = (Underlight::TripLine *)line;
          if (trip_line->event & Underlight::TripLine::Activate) {
            switch (trip_line->action) {
              case Underlight::TripLine::Teleport:
              {
                short x = trip_line->trip1;
                short y = trip_line->trip2;
                ///short min_rank = trip_line->trip3; // UNUSED
                short angle = trip_line->trip4;

                Underlight::Room* room = Underlight::room_from_pos (x, y);

                if (room != NULL) {
                  ((cli_parse::ModuleStates *)states->mod_states)->cl_player.Teleport (room->id, x, y, angle);
                } else {
                  // No room found...  BAD TELEPORTER!
                }
              } break;
              case Underlight::TripLine::LevelChange:
              {
                short x         = trip_line->trip1;
                short y         = trip_line->trip2;
                short angle     = trip_line->trip3;
                short level_num = trip_line->trip4 & 0xff;

                int lvl_num = level_num;

                Underlight::Room* room = Underlight::lvl->GetRoom (cli_parse::states->cl_cam_room);
                if (room)
                  room->Unload ();

                if (! states->LoadResources (&lvl_num)) {
                  //_tprintf (_T ("Could not load level: %d...\n"), cli_parse::states->cl_level_num);
                  eTB_Printf ("Could not load level: %d...\n", cli_parse::states->cl_level_num);
                  room->Load (Underlight::lvl);
                  //abort ();
                } else {
                  Underlight::Room* room = Underlight::room_from_pos (x, y);

                  if (room != NULL) {
                    ((cli_parse::ModuleStates *)states->mod_states)->cl_player.Teleport (room->id, x, y, angle);
                  } else {
//                  _tprintf (_T ("Unable to find the room [Level: %d :: (%d, %d)]\n"), level_num, x, y);
                    eTB_Printf ("Unable to find the room [Level: %d :: (%d, %d)]\n", level_num, x, y);
                    // No room found...  BAD TELEPORTER!
                  }

                  // DO THIS ON LEVEL LOAD
                  cli_parse::states->cl_picked_sector.room    = 0;
                  cli_parse::states->cl_picked_sector.surface = 0;

                  cli_parse::states->cl_picked_line.room    = 0;
                  cli_parse::states->cl_picked_line.surface = 0;
                }
              } break;
              default:
              {
                eMessageBox (_T ("Unusable Activatable Trip Line..."));
              } break;
            }
          }
        }
      }

      if (states->cl_verbose) {
        eTB_Printf ("Picked: ");
          pick.print ();
        eTB_Printf ("\n");
      }
    }

    return false;
  }

  if (((cli_parse::ModuleStates *)states->mod_states)->r_global_brightness > 0.0f &&
      ((cli_parse::ModuleStates *)states->mod_states)->r_global_brightness < 1.0f) {
    void draw_dark_overlay (float brightness);
    draw_dark_overlay (((cli_parse::ModuleStates *)states->mod_states)->r_global_brightness);
  }

  return true;
}

void CALLBACK HandleCamera_cli_parse (void)
{
  states->input->Update ();

  //e3dLoadIdentity  ();

  e3dCamera* cam = cli_parse::states->cl_player.Camera ();

  cam->Update (states->input);

  //_states.cl_cam_room;

  size_t size = Underlight::lvl->rooms.size ();

  e3dVector3 vPos = cam->GetPos ();

  int last_cam_room = cli_parse::states->cl_cam_room;

  for (int i = 0; (unsigned)i < size; i++) {
    e3dVector2 pos (vPos.x, vPos.y);

    if (Underlight::lvl->rooms [i]->bound.PointInBounds (pos)) {
      cli_parse::states->cl_cam_room = i;

      // Load the room if it's not already loaded...
      if (last_cam_room != i) {
        if (last_cam_room >= 0 && last_cam_room < MAX_ROOMS) {
          Underlight::Room* old_room = Underlight::lvl->rooms [last_cam_room];

          // Unload the room, iff cl_all_rooms is false.
          if (! cli_parse::states->cl_all_rooms)
            old_room->Unload ();

          cli_parse::states->cl_picked_sector.room    = 0;
          cli_parse::states->cl_picked_sector.surface = 0;

          cli_parse::states->cl_picked_line.room    = 0;
          cli_parse::states->cl_picked_line.surface = 0;
        }

        Underlight::Room* room = Underlight::lvl->rooms [i];
        room->Load (Underlight::lvl);
      }
      break;
    }
  }

  cli_parse::states->cl_player.Update ();

  /* Apply gravity when appropriate... */
  if (cli_parse::states->cl_gravity)
  {
    static eTimer timer;
    timer.tick ();

    if (! cli_parse::states->cl_player.OnGround ()) {
      float down = timer.FrameInterval () * 900.0f;

      e3dVector3 vPos = cli_parse::states->cl_player.Camera ()->GetPos ();
      vPos.z -= down;
      cli_parse::states->cl_player.Camera ()->SetPos (vPos.x, vPos.y, vPos.z);
    }
  }
}

void print_camera_pos (void)
{
  e3dVector3 pos = cli_parse::states->cl_player.Camera ()->GetPos ();
  eTB_Printf ("Camera Pos: (%f, %f)\n", pos.x, pos.y);
}